package com.mrx.commons.http;

import com.alibaba.fastjson2.JSON;
import com.alibaba.fastjson2.TypeReference;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.HttpEntity;
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.web.client.HttpServerErrorException;
import org.springframework.web.client.RestTemplate;

import javax.annotation.Nullable;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Optional;

/**
 * RestTemplate 的 工具类
 *
 * @author : Mr.X
 * @since : 2023-02-06 09:31
 **/
public class RestTemplateUtils {

    @Resource
    private RestTemplate restTemplate;
    private final String TOKEN_HEADER = "Authorization";
    private static final Logger logger = LoggerFactory.getLogger(RestTemplateUtils.class);

    /**
     * 向指定 url 发起一次 post 请求, 带上 token 请求头
     *
     * @throws RuntimeException 出现异常会抛出 RuntimeException
     */
    public <T> String postTokenJson(String url, String token, T obj) {
        return execute(url, buildEntity(obj, token));
    }

    /**
     * 向指定 url 发起一次 post 请求, 不带 token 请求头
     *
     * @param url 要请求的 url
     * @param obj 请求 body
     * @throws RuntimeException 出现异常会抛出 RuntimeException
     */
    public <T> String postJson(String url, T obj) {
        return execute(url, buildEntity(obj, null));
    }

    /**
     * 发起带 token 的 post 请求, 并将结果包装成对象<br/>
     * 这是
     *
     * <pre>{@code
     * String json = postTokenJson(url, token, body)
     * ResponseResult<Object> obj = JSON.parseObject(json, new TypeReference<ResponseResult<Object>>() {})
     * }</pre>
     * <p>
     * 的快捷方式
     *
     * @param url        post URL
     * @param token      post Token
     * @param body       post Body
     * @param resultType 预期 返回类型
     * @param <B>        Body 类型
     * @param <R>        返回值 类型
     * @return 包装后的 返回值
     */
    @Nullable
    public <B, R> R postTokenJsonForObject(String url, String token, B body, TypeReference<R> resultType) {
        String json = postTokenJson(url, token, body);
        return json2Entity(json, resultType);
    }

    /**
     * 使用 FastJSON 将 json 字符串转换为 TypeReference 对应的类型
     *
     * @param json       json 字符串
     * @param resultType TypeReference
     * @param <R>        真正的 Java 类型
     * @return 转换后的 Java 对象
     */
    @Nullable
    private <R> R json2Entity(String json, TypeReference<R> resultType) {
        if (json == null) {
            return null;
        }
        return JSON.parseObject(json, resultType);
    }

    /**
     * 使用 HttpEntity 向 url 发起一次 post 请求
     *
     * @param url    要请求的 url
     * @param entity 请求参数和请求体 封装的 HttpEntity
     * @param <T>    请求体类型
     * @return 请求结果, String 类型
     * @throws RuntimeException 出现异常会抛出 RuntimeException
     */
    private <T> String execute(String url, HttpEntity<T> entity) {
        try {
            logger.info("请求: {}, 入参: {}", url, entity);
            return restTemplate.postForObject(url, entity, String.class);
        } catch (Exception e) {
            if (e instanceof HttpServerErrorException) {
                String body = ((HttpServerErrorException) e).getResponseBodyAsString();
                logger.warn("请求出错, 错误响应: {}", body);
            }
            throw new RuntimeException("restTemplate 异常, url = " + url, e);
        }
    }

    /**
     * 使用 obj 作为 请求体, token 请求头 构造一个 HttpEntity
     *
     * @param obj   请求体
     * @param token 请求头里的 token, 可选, 没有就传 null
     * @param <T>   请求体数据类型
     * @return 请求结果, String 类型
     */
    private <T> HttpEntity<T> buildEntity(T obj, @Nullable String token) {
        HttpHeaders header = new HttpHeaders();
        header.setContentType(MediaType.APPLICATION_JSON);
        header.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON));
        Optional.ofNullable(token).ifPresent(it -> header.add(TOKEN_HEADER, it));
        return new HttpEntity<>(obj, header);
    }

}
